Repository navigation
Conversation
…viewDisplay - Extract @VisibleForTesting internal calculateViewfinderSize with non-positive bounds guard and landscape aspect ratio selection when useLandscapeAspectRatio && maxWidth > maxHeight - Add contentAlignment and useLandscapeAspectRatio trailing default parameters to PreviewDisplay - Replace BoxWithConstraints with standard Box + Modifier.layout measurement and graphicsLayer draw-phase blink alpha/clip - Stabilize gesture callbacks with rememberUpdatedState and migrate rememberTransformableState to non-deprecated centroid overload - Enforce LayoutDirection.Ltr inside PreviewDisplay viewfinder scope so tap-to-focus coordinates and FocusMeteringIndicator never mirror in RTL - Add PreviewDisplay @previews and Robolectric unit/Compose tests in PreviewDisplayTest
# Conflicts: # ui/components/capture/src/main/java/com/google/jetpackcamera/ui/components/capture/CaptureScreenComponents.kt
…dow orientation - Remove static android:screenOrientation="nosensor" from MainActivity in AndroidManifest.xml and declare orientation|screenSize|smallestScreenSize|screenLayout|keyboardHidden configChanges - Add runtime resolveRequestedOrientation in MainActivity to lock full-screen compact phones (< 600dp) to SCREEN_ORIENTATION_NOSENSOR while using SCREEN_ORIENTATION_UNSPECIFIED in multi-window and on large screens (>= 600dp) to prevent 1:1 OS compatibility letterboxing - Pass useLandscapeAspectRatio = true to PreviewDisplay in PreviewScreen so wide containers frame the viewfinder in landscape (4:3 / 16:9) - Add unit tests in MainActivityOrientationTest
…display-framing # Conflicts: # feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/PreviewScreen.kt # ui/components/capture/src/main/java/com/google/jetpackcamera/ui/components/capture/CaptureScreenComponents.kt
There was a problem hiding this comment.
Code Review
This pull request refactors orientation handling in MainActivity to dynamically adjust based on multi-window mode and screen size, and updates PreviewDisplay to support landscape aspect ratios, custom alignments, and improved accessibility semantics. It also adds comprehensive unit and Robolectric tests. A critical issue was identified in MainActivity where calling isInMultiWindowMode as a default parameter value will cause a crash on devices running below API level 24, requiring an explicit SDK version check.
|
|
||
| private fun updateRequestedOrientation( | ||
| configuration: Configuration, | ||
| multiWindowMode: Boolean = isInMultiWindowMode |
There was a problem hiding this comment.
Calling isInMultiWindowMode directly as a default parameter value will cause a NoSuchMethodError crash on devices running API levels below 24 (Android 7.0), as this property was introduced in API 24. Since the app's minimum SDK version is likely lower (e.g., API 21), this call must be guarded with an API level check.
multiWindowMode: Boolean = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
isInMultiWindowMode
} else {
false
}
Summary
Establishes the orientation and viewfinder-framing foundation for Adaptive Capture across multi-window panes, foldables, and tablets. Replaces
MainActivity's staticandroid:screenOrientation="nosensor"manifest lock with a posture-aware runtime policy (NOSENSORon full-screen< 600dphandhelds;UNSPECIFIEDin multi-window or>= 600dplarge screens) and upgradesPreviewDisplayto frame wide containers in landscape aspect ratios (4:3/16:9), run in a single layout pass withoutBoxWithConstraintssubcomposition, isolate optical coordinates from RTL mirroring, and support keyboard/TalkBack center-focus activation.Key Changes
AndroidManifest.xml,MainActivity.kt):android:screenOrientation="nosensor"and declaresandroid:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboardHidden"so rotations, multi-window resizes, and fold/unfold transitions do not recreateMainActivity(allowing active video recordings to continue across a foldable unfold).resolveRequestedOrientation(isInMultiWindowMode, smallestScreenWidthDp)(600dpthreshold) wired acrossonCreate,addOnMultiWindowModeChangedListener, andonConfigurationChanged.CaptureScreenComponents.kt,CaptureScaffold.kt):calculateViewfinderSize(...)to fitAspectRatio.toLandscapeFloat()whenuseLandscapeAspectRatio && maxWidth > maxHeight(preserving portrait ratios whenmaxWidth <= maxHeightand returningDpSize.Zerofor non-positive bounds).contentAlignment: Alignment = Alignment.TopCenteranduseLandscapeAspectRatio: Boolean = falsetoPreviewDisplay, and enablesuseLandscapeAspectRatio = trueinCaptureScaffold.BoxWithConstraints(SubcomposeLayout) with a standardBox+ single-passModifier.layoutreportingViewfinderTopLine, and moves capture-blink alpha +16.dpcorner clipping into draw-phaseModifier.graphicsLayer.CaptureScreenComponents.kt,strings.xml):LocalLayoutDirection provides LayoutDirection.Ltr.OnClick("Focus center"),.focusable, and hardware key activation (Enter,NumPadEnter,DirectionCenter,Spacebar) gated on>= 48.dpinteractive bounds, plus accessibleIndeterminateloading semantics.Technical Notes
!isInMultiWindowMode && smallestScreenWidthDp < 600) stay onSCREEN_ORIENTATION_NOSENSORso UI chrome can counter-rotate smoothly via sensor gravity without OS window-rotation animations. Multi-window panes andsw >= 600dpdevices useSCREEN_ORIENTATION_UNSPECIFIEDto prevent 1:1 OS compatibility letterboxing.ViewfinderTopLine: Measuring downstream modifiers (PREVIEW_DISPLAY,onGloballyPositionedforLocalOverlapTargetBounds,transformable,graphicsLayer) withConstraints.fixed(widthPx, heightPx)insideModifier.layoutguarantees overlap bounds and rounded-corner clipping track the exact visible frame while propagatingViewfinderTopLineupward through the outerBox.CameraXViewfinderandMutableCoordinateTransformeralways emit top-left physical coordinates; forcingLayoutDirection.LtrinsidePreviewDisplaykeepsFocusMeteringIndicator'sModifier.offsetaligned with physical tap points while preserving ambient RTL outside the viewfinder.Bugs Fixed Along the Way
FocusMeteringIndicatorrendering horizontally mirrored away from the physical tap point underLayoutDirection.Rtl.pointerInput(onFlipCamera)capturing staleonTapToFocuslambdas (and restarting mid-gesture ononFlipCameraupdates) andrememberTransformableStatecapturing staleonScaleZoomlambdas; migrated to the non-deprecated 4-parameterrememberTransformableStateoverload.try/finallyarounddelay(BLINK_TIME)so cancelled blink coroutines cannot leavePreviewDisplaystuck atalpha = 0f, suppressed zero-alpha strobing whenLocalDisableAnimationsistrue, and eliminated per-frame blink recompositions.NaN& Loading Spinner Hang: GuardedmaxWidth <= 0.dp || maxHeight <= 0.dpagainstNaNdivision and switched the loading indicator to a static0.75fprogress arc whenLocalDisableAnimationsistruewhile exposingR.string.camera_not_readysemantics.Not Included Yet
Preview.targetRotationand sensor-alignedViewPortsync (kim/adaptive/viewfinder-rotation).kim/adaptive/debug-layout-readout, PR add 3-signal rotation, recording lock, and debug layout readout #643).kim/adaptive/layouts->kim/adaptive/layout-rotation).W < HatwindowRotation = 90°/270°):PreviewDisplaycurrently picks3:4vs.4:3from pane bounds rather than oriented stream dimensions, center-cropping ~44% (4:3) to ~68% (16:9) of horizontal FOV relative to the saved photo; follow-up will drive framing fromSurfaceRequestdimensions/rotation so narrow panes letterbox cleanly (100% WYSIWYG).Testing
./gradlew :app:testDebugUnitTest --tests "*MainActivityOrientationTest"— 3 unit tests covering compact full-screen (411dp,599dp), compact multi-window, and large-screen/foldable (600dp,673dp) orientation policies../gradlew :ui:components:capture:testDebugUnitTest --tests "*PreviewDisplayTest"— 15 Robolectric unit & Compose UI tests coveringcalculateViewfinderSizeacross phone/tablet/foldable/zero-bound geometries, dynamic4:3↔16:9pillarbox/letterbox overlap bounds, LTR vs. RTL tap-to-focus parity,rememberUpdatedStatecallback refresh + pinch zoom, keyboard/TalkBack center-focus &< 48dpgating, and frame-by-frame capture blink alpha../gradlew :ui:components:capture:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.google.jetpackcamera.ui.components.capture.CaptureScreenComponentsTest— 3 EspressoAccessibilityValidatortests covering dark/light loading states and liveSurfaceRequestcenter-focus semantics.